package com.seu.me.test.end.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.FastDateFormat;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * @author liangfeihu
 * @since 2019/10/8 15:57
 */
@Slf4j
public class DateTimeUtil {
    /**
     * yyyy-MM-dd hh:mm:ss date format.
     */
    public static final String DATA_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DATA_TIME_FORMAT = "HH:mm:ss";

    /**
     * yyyyMMdd date format.
     */
    public static final String YYYYMMDD_DATA_FORMAT = "yyyyMMdd";

    public static final String YYYYMM_DATA_FORMAT = "yyyyMM";

    public static final String YYYYMMDD_H_DATA_FORMAT = "yyyy/MM/dd";

    /**
     * yyyy-MM-dd date format.
     */
    public static final String YYYY_MM_DD_DATA_FORMAT = "yyyy-MM-dd";
    public static final String YYYYMMDDHHMM_DATA_FORMAT = "yyyyMMddHHmm";
    public static final String YYYYMMDDHHMMSS_DATA_FORMAT = "yyyyMMddHHmmss";
    public static final String YYYYMMDD_HHMM_DATA_FORMAT = "yyyy-MM-dd HH:mm";
    public static final String YYYYMMDD_HHMMSS_DATA_FORMAT = "yyyy-MM-dd HH:mm:ss";

    public static final String API_CHANNEL_DATA_FORMAT = "ddMMyyyy HH:mm:ss";
    public static final String API_DATA_FORMAT_YYYY_MM_DD_HH_MM_SS = "yyyy-mm-dd hh:mm:ss";


    private static FastDateFormat dateFormat = FastDateFormat.getInstance(YYYY_MM_DD_DATA_FORMAT);

    /**
     * 按照"yyyy-MM-dd HH:mm:ss" 解析时间
     */
    public static Date parseTime(String dateString) {
        LocalDateTime localDateTime = parseDateTime(dateString, DATA_FORMAT);
        return fromLocalDateTime(localDateTime);
    }

    public static Date parseTime(String dateString, String pattern) {
        LocalDateTime localDateTime = parseDateTime(dateString, pattern);
        return fromLocalDateTime(localDateTime);
    }

    public static Date parseToDate(String dateString, String pattern) {
        LocalDate localDate = parseDate(dateString, pattern);
        return fromLocalDate(localDate);
    }

    /**
     * 按照"yyyy-MM-dd HH:mm:ss" 格式化时间
     */
    public static String formatDateTime(Date date) {
        return formatDateTime(date, DATA_FORMAT);
    }

    public static String formatTime(Date date) {
        return formatDateTime(date, DATA_TIME_FORMAT);
    }


    public static String formatDateTime(Long timestamp) {
        if (timestamp == null) {
            return null;
        }
        return formatDateTime(new Date(timestamp), YYYY_MM_DD_DATA_FORMAT);
    }

    /**
     * 按照指定格式生成时间字符串
     */
    public static String formatDateTime(LocalDate date, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return date.format(formatter);
    }

    /**
     * 按照指定格式生成时间字符串
     */
    public static String formatDateTime(LocalDateTime date, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return date.format(formatter);
    }

    /**
     * 按照指定格式生成时间字符串
     */
    public static String formatDateTime(Date date, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return toLocalDateTime(date).format(formatter);
    }

    /**
     * 按照指定格式指定时间
     */
    public static LocalDateTime parseDateTime(String dateTime, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return LocalDateTime.parse(dateTime, formatter);
    }

    /**
     * 按照指定格式指定日期
     */
    public static LocalDate parseDate(String dateTime, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return LocalDate.parse(dateTime, formatter);
    }

    public static LocalDateTime toLocalDateTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    public static Date fromLocalDateTime(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    public static Date fromLocalDate(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    /**
     * Calls {@link #asLocalDate(Date, ZoneId)} with the system default time zone.
     */
    public static LocalDate asLocalDate(Date date) {
        return asLocalDate(date, ZoneId.systemDefault());
    }

    /**
     * Creates {@link LocalDate} from {@code java.util.Date} or it's subclasses. Null-safe.
     */
    public static LocalDate asLocalDate(Date date, ZoneId zone) {
        if (date == null)
            return null;

        if (date instanceof java.sql.Date)
            return ((java.sql.Date) date).toLocalDate();
        else
            return Instant.ofEpochMilli(date.getTime()).atZone(zone).toLocalDate();
    }

    /**
     * Calls {@link #asLocalDateTime(Date, ZoneId)} with the system default time zone.
     */
    public static LocalDateTime asLocalDateTime(Date date) {
        return asLocalDateTime(date, ZoneId.systemDefault());
    }

    /**
     * Creates {@link LocalDateTime} from {@code java.util.Date} or it's subclasses. Null-safe.
     */
    public static LocalDateTime asLocalDateTime(Date date, ZoneId zone) {
        if (date == null)
            return null;

        if (date instanceof java.sql.Timestamp)
            return ((java.sql.Timestamp) date).toLocalDateTime();
        else
            return Instant.ofEpochMilli(date.getTime()).atZone(zone).toLocalDateTime();
    }

    /**
     * Calls {@link #asUtilDate(Object, ZoneId)} with the system default time zone.
     */
    public static Date asUtilDate(Object date) {
        return asUtilDate(date, ZoneId.systemDefault());
    }

    /**
     * Creates a {@link Date} from various date objects. Is null-safe. Currently supports:<ul>
     * <li>{@link Date}
     * <li>{@link java.sql.Date}
     * <li>{@link LocalDate}
     * <li>{@link LocalDateTime}
     * <li>{@link ZonedDateTime}
     * <li>{@link Instant}
     * </ul>
     *
     * @param zone Time zone, used only if the input object is LocalDate or LocalDateTime.
     * @return {@link Date} (exactly this class, not a subclass, such as java.sql.Date)
     */
    public static Date asUtilDate(Object date, ZoneId zone) {
        if (date == null)
            return null;

        if (date instanceof java.sql.Date || date instanceof java.sql.Timestamp)
            return new Date(((Date) date).getTime());
        if (date instanceof Date)
            return (Date) date;
        if (date instanceof LocalDate)
            return Date.from(((LocalDate) date).atStartOfDay(zone).toInstant());
        if (date instanceof LocalDateTime)
            return Date.from(((LocalDateTime) date).atZone(zone).toInstant());
        if (date instanceof ZonedDateTime)
            return Date.from(((ZonedDateTime) date).toInstant());
        if (date instanceof Instant)
            return Date.from((Instant) date);

        throw new UnsupportedOperationException("Don't know hot to convert " + date.getClass().getName() + " to java.util.Date");
    }

    /**
     * Creates an {@link Instant} from {@code java.util.Date} or it's subclasses. Null-safe.
     */
    public static Instant asInstant(Date date) {
        if (date == null)
            return null;
        else
            return Instant.ofEpochMilli(date.getTime());
    }

    /**
     * Calls {@link #asZonedDateTime(Date, ZoneId)} with the system default time zone.
     */
    public static ZonedDateTime asZonedDateTime(Date date) {
        return asZonedDateTime(date, ZoneId.systemDefault());
    }

    /**
     * Creates {@link ZonedDateTime} from {@code java.util.Date} or it's subclasses. Null-safe.
     */
    public static ZonedDateTime asZonedDateTime(Date date, ZoneId zone) {
        if (date == null)
            return null;
        else
            return asInstant(date).atZone(zone);
    }

    /**
     * Get off days of day1 and day2.
     */
    public static long offDaysBetweenDates(Date day1, Date day2) {
        LocalDate localDate1 = asLocalDate(day1);
        LocalDate localDate2 = asLocalDate(day2);
        if (localDate1.isBefore(localDate2)) {
            return localDate2.toEpochDay() - localDate1.toEpochDay();
        } else {
            return localDate1.toEpochDay() - localDate2.toEpochDay();
        }
    }

    public static Date startOfDay(Date date) {
        return fromLocalDateTime(asLocalDate(date).atStartOfDay());
    }

    public static Date plusDay(Date date, int days) {
        return fromLocalDateTime(asLocalDateTime(date).plusDays(days));
    }

    public static int getDayOfWeek() {
        Calendar now = Calendar.getInstance();
        return getDayOfWeekByDate(now);
    }

    public static int getDayOfWeekByDate(Calendar calendar) {
        boolean isFirstSunday = (calendar.getFirstDayOfWeek() == Calendar.SUNDAY);
        int weekDay = calendar.get(Calendar.DAY_OF_WEEK);
        if (isFirstSunday) {
            weekDay = weekDay - 1;
            if (weekDay == 0) {
                weekDay = 7;
            }
        }
        return weekDay;
    }

    /**
     * 获取当天时间的年月日，格式为yyyyMMdd，int类型
     *
     * @return
     */
    public static Integer getCurrentDate() {
        return Integer.parseInt(formatDateTime(LocalDate.now(), YYYYMMDD_DATA_FORMAT));
    }

    /**
     * 获取当前周的起始日期，格式为yyyyMMdd，int类型
     *
     * @return
     */
    public static Integer getWeekCurrentDate() {
        SimpleDateFormat format = new SimpleDateFormat(YYYYMMDD_DATA_FORMAT);
        Calendar c = Calendar.getInstance();
        c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        String weekStart = format.format(c.getTime());
        return Integer.parseInt(weekStart);
    }

    /**
     * 获取当前周的上一周起始日期，格式为yyyyMMdd，int类型
     *
     * @return
     */
    public static Integer getUpWeekCurrentStartDate() {
        SimpleDateFormat format = new SimpleDateFormat(YYYYMMDD_DATA_FORMAT);
        Calendar c = Calendar.getInstance();
        c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        c.add(Calendar.DATE, -7);
        String weekStart = format.format(c.getTime());
        return Integer.parseInt(weekStart);
    }

    /**
     * 获取当前周的上一周截止日期，格式为yyyyMMdd，int类型
     *
     * @return
     */
    public static Integer getUpWeekCurrentEndDate() {
        SimpleDateFormat format = new SimpleDateFormat(YYYYMMDD_DATA_FORMAT);
        Calendar c = Calendar.getInstance();
        c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        c.add(Calendar.DATE, -1);
        String weekStart = format.format(c.getTime());
        return Integer.parseInt(weekStart);
    }

    /**
     * 获取当前月的起始日期，格式为yyyyMMdd，int类型
     *
     * @return
     */
    public static Integer getMonthCurrentDate() {
        SimpleDateFormat format = new SimpleDateFormat(YYYYMMDD_DATA_FORMAT);
        Calendar c = Calendar.getInstance();
        c.set(Calendar.DAY_OF_MONTH, 1);
        String monthStart = format.format(c.getTime());

        return Integer.parseInt(monthStart);
    }

    /**
     * 获取当前季度的起始日期，格式为yyyyMMdd，int类型
     *
     * @return
     */
    public static Integer getQuarterCurrentDate() {
        SimpleDateFormat format = new SimpleDateFormat(YYYYMMDD_DATA_FORMAT);
        Calendar c = Calendar.getInstance();
        int currentMonth = c.get(Calendar.MONTH) + 1;
        if (currentMonth >= 1 && currentMonth <= 3)
            c.set(Calendar.MONTH, 0);
        else if (currentMonth >= 4 && currentMonth <= 6)
            c.set(Calendar.MONTH, 3);
        else if (currentMonth >= 7 && currentMonth <= 9)
            c.set(Calendar.MONTH, 4);
        else if (currentMonth >= 10 && currentMonth <= 12)
            c.set(Calendar.MONTH, 9);
        c.set(Calendar.DATE, 1);
        return Integer.parseInt(format.format(c.getTime()));
    }

    /**
     * 获取两时间内的天数
     *
     * @return
     */
    public static List<Integer> getBetDays(Integer stime, Integer etime) {
        List<Integer> ls = new ArrayList<>();

        LocalDate sDate = parseDate(stime.toString(), YYYYMMDD_DATA_FORMAT);

        LocalDate eDate = parseDate(etime.toString(), YYYYMMDD_DATA_FORMAT);

        while (stime <= etime) {
            ls.add(Integer.parseInt(formatDateTime(sDate, YYYYMMDD_DATA_FORMAT)));

            sDate = sDate.plusDays(1);

            stime = Integer.parseInt(formatDateTime(sDate, YYYYMMDD_DATA_FORMAT));
        }

        return ls;
    }

    /**
     * 获取两时间内的天数,格式为yyyy-MM-dd
     *
     * @return
     */
    public static List<String> getBetDays(String stime, String etime) {
        List<String> ls = new ArrayList<>();

        if (stime.equals(etime)) {
            ls.add(stime);
            return ls;
        }

        LocalDate sDate = parseDate(stime, YYYY_MM_DD_DATA_FORMAT);

        LocalDate eDate = parseDate(etime, YYYY_MM_DD_DATA_FORMAT);

        if (eDate.compareTo(sDate) < 0) return null;

        while (!stime.equals(etime)) {
            ls.add(formatDateTime(sDate, YYYY_MM_DD_DATA_FORMAT));

            sDate = sDate.plusDays(1);

            stime = formatDateTime(sDate, YYYY_MM_DD_DATA_FORMAT);
        }

        ls.add(etime);
        return ls;
    }

    public static List<String> getMonthBetween(String minDate, String maxDate) {
        ArrayList<String> result = new ArrayList<String>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");//格式化为年月

        Calendar min = Calendar.getInstance();
        Calendar max = Calendar.getInstance();

        try {
            min.setTime(sdf.parse(minDate));
            min.set(min.get(Calendar.YEAR), min.get(Calendar.MONTH), 1);

            max.setTime(sdf.parse(maxDate));
            max.set(max.get(Calendar.YEAR), max.get(Calendar.MONTH), 2);
        } catch (Exception e) {

        }

        Calendar curr = min;
        while (curr.before(max)) {
            result.add(sdf.format(curr.getTime()));
            curr.add(Calendar.MONTH, 1);
        }

        return result;
    }

    public static Long toLongTime(Date date) {
        if (date == null) {
            return null;
        }
        return date.getTime();

    }
}
